home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
PROGRAMM
/
CC_C
/
0294.ZIP
/
SHELL2.ARC
/
CMDS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1985-08-08
|
19KB
|
1,035 lines
#include <dos.h>
#include <stdio.h>
#include <direct.h>
#include <string.h>
#include <process.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <setjmp.h>
#include "gen.h"
#include "sh.h"
/*
* sh: a Unix(tm) like command interpreter for MS-DOS.
*
* by Douglas Orr
* Textset Inc.
* Ann Arbor, Michigan
*
* Copyright (c) 1985 Textset. All rights reserved.
*
* This program may be freely distributed, but not sold for profit.
*
*/
/* declare built-in functions */
Public b_ls(), b_echo(), b_pushd(), b_popd();
Public b_pwd(), b_cd(), b_mkdir(), b_rmdir(), b_rm();
Public b_history(), b_exit(), b_dirs(), b_rehash();
Public b_set(), b_fgrep(), b_source();
Public int fcmp();
/* disk transfer area */
typedef union dta
{
struct {
char dt_dos[21];
#define DosSub (0x10)
#define DosHid (0x02)
byte dt_attr;
short dt_time;
short dt_date;
unsigned short dt_size_low;
unsigned short dt_size_high;
char dt_name[13];
} d;
short buf[128];
} Dta;
Local Dta dta;
/*
* give us an easily addressable disk transfer area
*/
set_dta()
{
union REGS regs;
union SREGS segregs;
Dta far * dta_addr = &dta;
regs.h.ah = 0x1a; /* set dta */
regs.x.dx = FP_OFF(dta_addr);
segregs.ds = FP_SEG(dta_addr);
intdosx( ®s, ®s, &segregs );
}
/*
* set our disk transfer area and match the first file in the given
* path.
* !! Warning: uSoft routines that do disk accesses reset the
* disk transfer area. Don't call in between directory reads
* examples: open, stat, etc.
*
* ?! add flags to allow optionally getting hidden/volume file names
*/
char *
open_dir( ptr )
char * ptr;
{
char far * fptr;
union REGS regs;
union SREGS segregs;
set_dta();
fptr = ptr;
regs.h.ah = 0x4e; /* find first entry */
regs.x.dx = FP_OFF(fptr);
segregs.ds = FP_SEG(fptr);
regs.x.cx = DosSub; /* look for normal files & subdirectories*/
intdosx( ®s, ®s, &segregs );
if( regs.x.cflag )
return( NULL );
else
/* name of the current file in the dta */
return( dta.d.dt_name );
}
char *
nxt_entry( ptr )
char * ptr;
{
union REGS regs;
regs.h.ah = 0x4f; /* find next entry */
regs.x.cx = DosSub; /* look for normal files & subdirs */
intdos( ®s, ®s );
if( regs.x.cflag )
return( NULL );
else
return( dta.d.dt_name );
}
/* extract fields from dta */
dta_mode()
{
char * ptr;
int mode = 0;
if( (ptr = strrchr( dta.d.dt_name, '.' )) )
{
if( (strcmpi( ptr, ".bat" ) == 0)
|| (strcmpi( ptr, ".exe" ) == 0)
|| (strcmpi( ptr, ".com" ) == 0) )
mode |= S_IEXEC;
}
if( dta.d.dt_attr & DosSub )
mode |= S_IFDIR;
return( mode );
}
/*
* return most of the time info provided in struct tm
*/
struct tm *
dta_time()
{
Local struct tm dta_time;
dta_time.tm_hour = (dta.d.dt_time >> 11) & 0x1f;
dta_time.tm_min = (dta.d.dt_time >> 5) & 0x3f;
dta_time.tm_sec = (dta.d.dt_time & 0x1f) * 2;
dta_time.tm_year = ((dta.d.dt_date >> 9) & 0x7f) + 80;
dta_time.tm_mon = ((dta.d.dt_date >> 5) & 0xf);
dta_time.tm_mday = (dta.d.dt_date & 0x1f);
if( dta_time.tm_mon > 12 || dta_time.tm_mon < 1 )
dta_time.tm_mon = 0;
return( &dta_time );
}
long
dta_size()
{
return( (((long)dta.d.dt_size_high) << 16) + dta.d.dt_size_low );
}
/* "disk" operations */
/*
* change the current disk
*/
chdsk( disk )
int disk;
{
union REGS regs;
if( isupper(disk) )
disk -= 'A';
else
disk -= 'a';
if( disk < 0 || disk > 25 )
{
fprintf( stderr, "invalid drive\n" );
return( -1 );
}
regs.h.ah = 0x0e; /* change disk */
regs.h.dl = disk; /* to this value */
intdos( ®s, ®s );
return( 0 );
}
bool
isdev( disk )
char * disk;
{
return( (strlen(disk) == 2) && (disk[1] == ':') );
}
getcdsk()
{
union REGS regs;
regs.h.ah = 0x19; /* get current disk */
intdos( ®s, ®s );
return( regs.h.al + 'a' );
}
#define MaxCmdLen (15)
#define Cm_Builtin (0x01)
#define Cm_Batch (0x02)
typedef struct cmds
{
char cm_name[MaxCmdLen];
char * cm_path;
short cm_flags;
int (* cm_rtne)();
} Cmds;
#define MaxCmds (256)
Local Cmds cmds[MaxCmds];
/* built-in commands */
Local struct bi {
char * bi_name;
int (* bi_rtne)();
}
builtins[] =
{
"ls", b_ls,
"echo", b_echo,
"cd", b_cd,
"pwd", b_pwd,
"pushd", b_pushd,
"pd", b_pushd,
"popd", b_popd,
"rm", b_rm,
"mkdir", b_mkdir,
"rmdir", b_rmdir,
"history", b_history,
"exit", b_exit,
"dirs", b_dirs,
"rehash", b_rehash,
"set", b_set,
"fgrep", b_fgrep, /* msdos sucks */
"source", b_source, /* just like on MTS */
NULL, NULL,
};
Local int cmdcnt = 0;
/* Command Parsing Stuff */
add_cmd( cmd, path, flags, rtne )
char * cmd;
char * path;
short flags;
int (* rtne)();
{
if( cmdcnt >= MaxCmds )
{
fprintf( stderr, "error: hash table overflow\n" );
return;
}
/* ?! make this into a real hash table, eventually */
strcpy( cmds[cmdcnt].cm_name, cmd );
if( path )
cmds[cmdcnt].cm_path = strdup(path);
else
cmds[cmdcnt].cm_path = NULL;
cmds[cmdcnt].cm_flags = flags;
cmds[cmdcnt].cm_rtne = rtne;
cmdcnt++;
}
/*
* ... ok ... so we're not really hashing anything
*/
rehash( path )
char * path;
{
char mypath[128];
char buf[128];
char cbuf[128];
Reg char * pptr;
char * ptr;
char * extptr;
char * cur_path;
int flags;
int i;
/* ?! make this into a real hash table, eventually */
for( i=0; i<cmdcnt; i++ )
{
if( cmds[i].cm_path )
free( cmds[i].cm_path );
}
cmdcnt = 0;
/* add "built in" commands */
for( i=0; builtins[i].bi_name; i++ )
add_cmd( builtins[i].bi_name, NULL, Cm_Builtin,
builtins[i].bi_rtne );
/* add the stuff in the path */
for( pptr=path; *pptr; )
{
if( *pptr == ';' )
++pptr;
cur_path = pptr++;
while( (*pptr != ';') && (*pptr != '\0') )
++pptr;
strncpy( mypath, cur_path, pptr-cur_path );
mypath[pptr-cur_path] = '\0';
/* open the directory */
strcat( mypath, "\\*.*" );
ptr = open_dir( mypath );
mypath[ strlen(mypath)-4 ] = '\0';
/* find everything that looks executeable */
for( ; ptr; ptr = nxt_entry() )
{
strlwr( ptr );
if( !(extptr = strrchr(ptr, '.'))
|| (strcmp(extptr, ".exe") != 0
&& strcmp(extptr, ".bat") != 0
&& strcmp(extptr, ".com") != 0) )
continue;
flags = 0;
strncpy( cbuf, ptr, extptr-ptr );
cbuf[extptr-ptr] = '\0';
sprintf( buf, "%s\\%s", mypath, ptr );
if( strcmp( extptr, ".bat" ) == 0 )
flags |= Cm_Batch;
add_cmd( cbuf, buf, flags, NULL );
}
}
}
#define MaxArgs (128)
Local char * argv[MaxArgs];
/*
* duplicate arg string, turning off all high bits
*/
char *
astrdup(str)
char * str;
{
Reg char * ptr;
char * val;
val = ptr = strdup(str);
while( *ptr )
*ptr++ &= 0x7f;
return( val );
}
/*
* dissect breaks the command into arguments
*
* !! ASSUME getbuf "escape"s all quotes within quotes, so quote
* processing is trivial at this point
*/
char * *
dissect( buf, bufsize )
char * buf;
int bufsize;
{
int done = False;
int argc = 0;
int i;
char * bufend;
char * wordend;
/* ?! it might be better to store history as argv lists, and
* do substitution after initial arg breakup
*/
i = strlen(buf);
if( i && buf[i-1] == '\n' )
buf[i-1] = '\0';
if( !sub_hist( buf, bufsize ) )
return( NULL );
/* plant this stuff, for batch and mystery commands */
argv[argc++] = "\\command";
argv[argc++] = "/c";
bufend = buf+bufsize;
while( !done )
{
while( isspace(*buf) )
buf++;
if( *buf ) {
if( argc == MaxArgs )
{
fprintf(stderr, "error: too many arguments\n");
return( NULL );
}
if( *buf == '\'' || *buf == '"' )
{
/* word starts on next character */
i = *buf++;
wordend = buf;
while( (*wordend != i) && *wordend )
wordend++;
}
else
{
/* normal space delimited word */
wordend = buf;
while( (!isspace(*wordend)) && *wordend )
wordend++;
}
if( !*wordend )
done = True;
/* !! assuming at least one space between args */
*wordend = '\0';
/* can we do a wildcard substitution on this? */
if( iswild( buf ) )
{
if( (i = do_wildcard( buf, argv, argc )) == 0)
{
fprintf( stderr, "couldn't match %s\n",
buf );
return( NULL );
}
else
argc += i;
}
else
argv[argc++] = astrdup(buf);
buf = wordend+1;
}
else
done = True;
}
argv[argc] = NULL;
/* make with the I/O redirections */
if( (argc = do_redirect( argc, argv )) < 0 )
{
fprintf( stderr, "redirection error\n" );
return( NULL );
}
if( argc == 2 )
return( NULL );
return( argv+2 );
}
/* ?! Add []{} eventually */
iswild( buf )
char * buf;
{
while( *buf )
{
if( *buf++ == '*' )
return( True );
}
return( False );
}
/* ?! Add []{} eventually */
do_wildcard( pat, argv, argc )
char * pat;
char * * argv;
int argc;
{
char * * av;
char * ptr;
char * cwd;
char tmp[128];
char * endpat;
int tmpch;
int wargc;
int dev = -1;
av = argv + argc;
cwd = NULL;
/*
* ?! currently only handle one level of wildcard - FIXIT
* UGLY ALERT
*/
if( (ptr = strrchr( pat, '/' ))
|| (ptr = strrchr( pat, '\\' )) )
{
/*
* save the directory spec, with which to rebuild file
* names later
*/
strncpy( tmp, pat, ptr-pat+1 );
endpat = tmp + (ptr-pat) + 1;
tmpch = *ptr;
*ptr = '\0';
if( pat[1] == ':' )
{
dev = getcdsk();
if( chdsk( pat[0] ) == -1 )
goto bad_end;
pat += 2;
}
/* go to the directory you wish to examine */
if( (cwd = getcwd( NULL, 128 )) == NULL )
goto bad_end;
if( ptr != pat )
{
if( chdir( pat ) == -1 )
{
fprintf( "%s: no such directory\n", pat );
goto bad_end;
}
}
else
chdir( "/" );
*ptr = tmpch; /* back the way they were */
pat = ptr+1;
}
else
endpat = tmp;
/* save the pattern, and delete it from the buffer */
for( ptr=open_dir( "*.*" ); ptr; ptr = nxt_entry() )
{
strlwr(ptr);
if( ismatch( pat, ptr ) )
{
if( av-argv >= MaxArgs )
{
fprintf( stderr, "%d: too many arguments\n",
av-argv );
goto bad_end;
}
if( ptr[0] == '.' && pat[0] != '.' )
continue;
strcpy( endpat, ptr );
*av++ = strdup(tmp);
}
}
*av = NULL;
if( cwd )
{
chdir( cwd );
free( cwd );
}
/* sort the wildcarded arguments arguments */
/* !! this can overflow your stack if you are unlucky */
wargc = av - (argv+argc);
if( wargc > 1 )
qsort( (char *)(argv+argc), wargc, sizeof(char *), fcmp );
if( dev != -1 )
chdsk(dev);
return( wargc );
/* he was a bad boy, and he came to a bad end */
bad_end:
if( dev != -1 )
chdsk(dev);
return( 0 );
}
/* perform I/O redirections */
do_redirect( argc, argv )
int argc;
char * * argv;
{
int i, j;
int skip;
char * file;
char * mode;
FILE * fd;
int direct;
int mod;
/*
* moderate UGLY ALERT
*/
for( i=2; i<argc; )
{
if( ((direct = argv[i][0]) == '>') || (direct == '<') )
{
skip=1;
file = argv[i]+1;
mod = '\0';
if( (*file == '>') || (*file == '&') )
mod = *file++;
/* is file name in the second argument? */
if( *file == '\0' )
{
if( i == argc-1 )
return( -1 );
skip=2;
file = argv[i+1];
}
if( direct == '<' )
{
mode = "r";
fd = stdin;
}
else
{
mode = "w";
fd = stdout;
if( mod == '>' )
mode = "a";
}
if( freopen( file, mode, fd ) == NULL )
return( -1 );
/* >& gets stderr & stdout */
if( mod == '&' )
dup2( 1, 2 );
/* UGLY part */
for( j=i; j+skip <= argc ; j++ )
argv[j] = argv[j+skip];
argc -= skip;
}
else
i++;
}
return( argc );
}
/*
* perform history substitutions
*/
/* ?! add :modifiers, and ^^^ substitutions */
#define HistMax (50)
short hindex[HistMax];
char * history[HistMax] = { NULL, };
short hind = 0;
short hmax = HistMax;
short hcount = 0;
sub_hist( buf, bufsize )
char * buf;
int bufsize;
{
char * hptr;
char * hend;
char * sub = NULL;
int ind;
int hval;
char tmpchr;
int i;
char * to, * from;
int sublen;
int cmdlen;
for( hptr=buf; (hptr = strchr( hptr, '!' )); )
{
sub = NULL;
ind = hind-1;
if( ind < 0 )
ind = hmax-1;
if( *(hptr+1) == '!' )
hend = hptr+2;
else
{
for( hend=hptr+1;
*hend && !(isspace(*hend) || ispunct(*hend));
hend++)
/* find end of history spec */;
}
/* case !! */
if( *(hptr+1) == '!' )
{
/* !! -> insert previous command */
if( (sub = history[ind]) == NULL )
goto noevent;
}
else
/* case !num */
if( isdigit(*(hptr+1)) )
{
/* !## -> insert command ## */
tmpchr = *hend;
*hend = '\0';
hval = atoi( hptr+1 );
*hend = tmpchr;
for( i=0; i<hmax; i++ )
{
if( ind < 0 )
ind = hmax-1;
if( history[ind] == NULL ) break;
if( hindex[ind] == hval )
{
sub = history[ind];
break;
}
ind--;
}
}
else
/* !str -> insert history string starting with "str" */
{
for( i=0; i<hmax; i++ )
{
if( ind < 0 )
ind = hmax-1;
if( history[ind] == NULL ) break;
if( strncmp( hptr+1, history[ind], (hend-hptr)-1 ) == 0 )
{
sub = history[ind];
break;
}
ind--;
}
}
if( sub == NULL )
{
noevent:
fprintf(stderr,"%.*s: event not found\n",
hend-hptr-1, hptr+1);
return( False );
}
else
{
/* perform substitution */
sublen = strlen(sub);
cmdlen = strlen(buf);
if( (cmdlen - (hend-hptr) + sublen) > bufsize )
{
fprintf( stderr, "event too large\n" );
return( False );
}
/* make a null terminated string out of this */
tmpchr = *hend;
*hend = '\0';
if( cmdlen - (hend-hptr) + sublen > bufsize )
{
fprintf( stderr, "substitution too large\n" );
return( False );
}
/* do the substitution */
insert(hptr,hend-hptr,(buf+cmdlen)-hptr,sub,sublen);
/* replace the original character */
*(hptr+sublen) = tmpchr;
}
}
/* retire old buffer if we have wrapped around */
if( history[hind] )
free( history[hind] );
/* record this buffer for future histories */
hindex[hind] = ++hcount;
history[hind++] = astrdup( buf );
if( hind >= hmax )
hind = 0;
/* echo substituted string */
if( sub )
fprintf( stderr, "%s\n", buf );
return( True );
}
/* UGLY ALERT */
insert( old, oldlen, totlen, new, newlen )
char * old; /* history string */
int oldlen; /* size of history string */
int totlen; /* length from start of history to end of command */
char * new; /* replacement string */
int newlen; /* size of replacement string */
{
Reg char * from, * to;
int i;
/* do the substitution */
if( newlen < oldlen )
{
/* shift left */
to = old+newlen;
from = old+oldlen;
while( (*to++ = *from++) )
/* shift, shift, shift, shift */;
}
else
if( newlen > oldlen )
{
/* shift right */
from = old+totlen; /* start with trailing null */
to = from + (newlen - oldlen);
for( i = (newlen-oldlen); i-- >= 0; )
*to-- = *from--;
}
/* stick in the new command */
from = new;
to = old;
for( i=0; i<newlen; i++ )
*to++ = *from++;
}
jmp_buf sbuf;
sig_handle()
{
/* reset the interrupt signal */
signal( SIGINT, SIG_IGN );
/* bounce back */
longjmp( sbuf, True );
}
/* locate the appropriate executable command and doit */
cmd( argv, envp )
char * * argv;
char * * envp;
{
Reg int len;
Reg int i;
char * cmdptr;
char * ptr;
int rc;
int batch;
struct stat statb;
cmdptr = NULL;
batch = False;
/* is this a specific path reference */
if( strchr( argv[0], '/' ) == NULL
&& strchr( argv[0], '\\' ) == NULL )
{
len = strlen( argv[0] );
/* is this really a drive selection? */
if( (len == 2) && (argv[0][1] == ':') )
return( chdsk( argv[0][0] ) );
/*
* check . first ... UGLY ALERT (a real hashing scheme
* could take care of this a little more nicely)
*/
if( stat( argv[0], &statb ) == -1
|| (statb.st_mode & S_IEXEC) == 0 )
{
/* not in . is it in the "hash" table? */
for( i=0; i<cmdcnt; i++ )
{
if( len == strlen( cmds[i].cm_name )
&& (strncmp( cmds[i].cm_name, argv[0], len ) == 0 ))
break;
}
if( i < cmdcnt )
{
cmdptr = cmds[i].cm_path;
/* a built in command? */
if( cmds[i].cm_flags & Cm_Builtin )
{
if( setjmp(sbuf) )
{
fprintf( stderr, "SH: interrupt\n" );
return( -1 );
}
signal( SIGINT, sig_handle );
rc = (*cmds[i].cm_rtne)(argv,envp);
signal( SIGINT, SIG_IGN );
return( rc );
}
else
batch = (cmds[i].cm_flags & Cm_Batch);
}
}
}
else
{
/* some sort of a specific path invocation */
cmdptr = argv[0];
/* reality check */
if( (ptr = strrchr( cmdptr, '.' )) )
{
if( strcmp( ptr, ".bat" ) == 0 )
batch = True;
}
}
if( cmdptr && !batch )
{
/* invoke the specific command */
if( (rc = spawnve( P_WAIT, cmdptr, argv, envp )) < 0)
perror( argv[0] );
return( rc );
}
else
{
/*
* let DOS take a crack at it - this is not in the true Unix
* tradition, but pragmatically, it works out well for
* stuff like "mode," etc.
*
* !! IMPORTANT - argv always has arguments "\command" "/c"
* as argv[-2,-1]
*/
if( (rc = spawnve( P_WAIT, "\command", argv-2, envp )) < 0)
perror( argv[0] );
return( rc );
}
}